# -*- coding: utf-8 -*-

from com.maycur import config_loader
from com.maycur.grpc.base.java_loader import JavaClassInfo
from com.maycur.grpc.base.java_loader import JavaLoader
from com.maycur.urlpermission.url_meta_info_sql import UrlMetaSqlGenerator, ControllerInfo
from com.maycur.urlpermission.url_meta_info_sql import UrlMetaInfo
from javalang.tree import Annotation
from typing import List
from com.maycur.utils import TimeUtils

default_request_methods = ['GET', 'POST', 'PUT', 'DELETE']


class UrlWithPermCollector:
    root_path: str
    controller_loader: JavaLoader
    perm_controller_list: List[ControllerInfo]
    separate_request_mapping_controllers: List[ControllerInfo]
    all_commented_classes: List[JavaClassInfo]
    sql_generator: UrlMetaSqlGenerator

    def __init__(self, change_set_id_prefix, change_set_id_start_index, change_set_author):
        config = config_loader.get_url_perm_config()
        self.root_path = config.project_path + config.root_path
        self.controller_loader = JavaLoader()
        self.perm_controller_list = []
        self.separate_request_mapping_controllers = []
        self._collect_url_with_perm_annotation()
        self.sql_generator = UrlMetaSqlGenerator(change_set_id_prefix, change_set_id_start_index, change_set_author, None)

    def generate_sql(self):
        self.sql_generator.parse_by_controllers(self.perm_controller_list)
        self.sql_generator.generate_url_meta_info_sql()
        self.sql_generator.generate_url_perm_relation_sql()

    def _collect_all_controller(self):
        self.controller_loader.load_by_package(self.root_path)

    def _collect_url_with_perm_annotation(self):
        self._collect_all_controller()
        for item in self.controller_loader.classes:
            if hasattr(item, 'annotation_set'):
                base_request_mapping = ''
                for annotation_item in item.raw_annotation_list:
                    if annotation_item.name == 'RequestMapping' and annotation_item.element is not None:
                        base_request_mapping = annotation_item.element.value.replace('"', '')
                for annotation_item in item.annotation_set:
                    if annotation_item in ['Controller', 'RestController']:
                        api_list = self._collect_perm_api_in_controller(item)
                        controller_info = ControllerInfo(item.full_name, base_request_mapping, api_list)
                        if len(api_list) > 0:
                            self.perm_controller_list.append(controller_info)
                        if base_request_mapping != '':
                            self.separate_request_mapping_controllers.append(controller_info)

    def _collect_perm_api_in_controller(self, item: JavaClassInfo) -> List[UrlMetaInfo]:
        api_list = []
        for method_item in item.methods:
            method_declaration = method_item.declaration
            method_annotations = method_declaration.annotations
            # api的修饰符均为public
            if 'public' not in method_declaration.modifiers:
                continue
            module_permissions = []
            for annotation_item in method_annotations:
                if annotation_item.name == 'RequiresPermissions':
                    module_permissions = self._parse_module_permissions(annotation_item)
                    break
            if len(module_permissions) == 0:
                continue
            for annotation_item in method_annotations:
                if annotation_item.name in ['GetMapping', 'PostMapping', 'PutMapping', 'DeleteMapping', 'RequestMapping']:
                    if annotation_item.name == 'RequestMapping':
                        url_meta_info_list = self._parse_request_mapping_api(annotation_item)
                    else:
                        url_meta_info_list = self._parse_rest_api(annotation_item)

                    # 解析方法注释
                    documentation = ''
                    if hasattr(method_item.declaration, 'documentation'):
                        documentation = self._parse_method_document(method_declaration.documentation)
                    for url_meta_item in url_meta_info_list:
                        url_meta_item.comment = documentation
                        url_meta_item.modules = module_permissions
                        api_list.append(url_meta_item)

        return api_list

    @staticmethod
    def _parse_request_mapping_api(request_mapping_annotation: Annotation) -> List[UrlMetaInfo]:
        res = []
        value_pair = request_mapping_annotation.children[1]
        if hasattr(value_pair, 'value'):
            # @RequestMapping("/web/currency/base"), 默认取GET/POST/PUT/DELETE
            url = value_pair.value.replace('"', '')
            for item in default_request_methods:
                res.append(UrlMetaInfo(None, url, item, None))
        elif hasattr(value_pair, 'values'):
            # 形如：@RequestMapping({"/web/org/department/isCompany", "/mkh5/web/org/department/isCompany"})
            for item in value_pair.values:
                # 请求方式默认取GET/POST/PUT/DELETE
                for request_m_item in default_request_methods:
                    res.append(UrlMetaInfo(None, item.value.replace('"', ''), request_m_item, None))
        else:
            url_list = []
            if hasattr(value_pair[0].children[1], 'values'):
                for url_item in value_pair[0].children[1].values:
                    url_list.append(url_item.value.replace('"', ''))
            else:
                url_list.append(value_pair[0].children[1].value.replace('"', ''))
            if len(value_pair) >= 2:
                if hasattr(value_pair[1].children[1], 'values'):
                    member_references = value_pair[1].children[1].children[0]
                    for member_item in member_references:
                        for url_item in url_list:
                            res.append(UrlMetaInfo(None, url_item, member_item.member, None))
                else:
                    for url_item in url_list:
                        res.append(UrlMetaInfo(None, url_item, value_pair[1].value.member, None))
            else:
                # @RequestMapping(value={"/web/payment/scene/calculation", "/mkh5/web/payment/scene/calculation"})
                # 请求方式默认取GET/POST/PUT/DELETE
                for url_item in url_list:
                    for request_m_item in default_request_methods:
                        res.append(UrlMetaInfo(None, url_item, request_m_item, None))
        return res

    @staticmethod
    def _parse_rest_api(rest_api_annotation: Annotation) -> List[UrlMetaInfo]:
        url_list = []
        if hasattr(rest_api_annotation.children[1], 'value'):
            url_list.append(rest_api_annotation.children[1].value.replace('"', ''))
        else:
            if hasattr(rest_api_annotation.children[1], 'values'):
                # @PutMapping({"/web/expense/edit", "/mkh5/web/expense/edit"})
                url_values = rest_api_annotation.children[1].values
            elif hasattr(rest_api_annotation.children[1][0].value, 'values'):
                # @GetMapping(value = {"/web/test/{legalEntityCode}", "/mkh5/web/test/{legalEntityCode}"})
                url_values = rest_api_annotation.children[1][0].value.values
            else:
                # @PostMapping(path = "/web/huabei/apply")
                url_values = [rest_api_annotation.children[1][0].value]
            for url_item in url_values:
                url_list.append(url_item.value.replace('"', ''))
        annotation_name = rest_api_annotation.name
        if annotation_name == 'GetMapping':
            request_method = 'GET'
        elif annotation_name == 'PostMapping':
            request_method = 'POST'
        elif annotation_name == 'PutMapping':
            request_method = 'PUT'
        else:
            request_method = 'DELETE'
        res = []
        for url_item in url_list:
            res.append(UrlMetaInfo(None, url_item, request_method, None))
        return res

    @staticmethod
    def _parse_method_document(origin_document: str) -> str:
        if origin_document is None:
            return ''
        document_lines = origin_document.split('\n')
        extract_lines = []
        for line_item in document_lines:
            processed_line = line_item.replace('/', '').replace('*', '').strip()
            if processed_line != '' and '@param' not in processed_line and '@return' not in processed_line:
                extract_lines.append(processed_line)
        if len(extract_lines) == 0:
            return ''
        else:
            return ' '.join(extract_lines)

    @staticmethod
    def _parse_module_permissions(annotation_item: Annotation) -> List[str]:
        res = []
        annotation_ele = annotation_item.element
        if type(annotation_ele) == list:
            for element_item in annotation_ele:
                if element_item.name == 'value':
                    for permission_item in element_item.value.values:
                        res.append(permission_item.member)
        else:
            res.append(annotation_ele.member)
        return res


if __name__ == '__main__':
    # Liquibase changeSet id前缀
    id_prefix = 'maycur-gateway-plugin-20220502-dml'
    # Liquibase changeSet id后缀下标
    id_start_index = 0
    # Liquibase changeSet author
    author = 'liangtj'

    start = TimeUtils.current_timestamp()
    collector = UrlWithPermCollector(id_prefix, id_start_index, author)

    all_commented_classes = collector.controller_loader.all_commented_classes
    if len(all_commented_classes) > 0:
        print('\n\n\n')
        print('以下文件已经全部注释掉，可以考虑清理\n')
        for class_item in all_commented_classes:
            print(class_item)

    separate_controllers = collector.separate_request_mapping_controllers
    if len(separate_controllers) > 0:
        print('\n\n\n')
        print('以下Controller的@RequestMapping需要下沉到方法上\n')
        for item in collector.separate_request_mapping_controllers:
            print(item.name)
    print('\n\n\n')
    collector.generate_sql()

    print(f'total process time: {TimeUtils.current_timestamp() - start} ms')
